package org.acme.server.manager;

import org.acme.latch.MySqlLatch;
import org.acme.latch.MysqlLatchListener;
import org.acme.server.po.SysServer;
import org.acme.job.service.SysJobService;
import org.acme.server.service.SysServerService;
import org.acme.server.utils.CollectionUtils;
import org.acme.server.utils.TimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author chentao
 * @version 1.0
 * @description: TODO
 * @date 2025/3/7 下午11:06
 */
@Service
public class SysServerManagerImpl implements SysServerManager, MysqlLatchListener, InitializingBean, DisposableBean {

    Logger logger = LoggerFactory.getLogger(getClass());

    @Resource
    private SysServerService sysServerService;

    @Resource
    private SysJobService sysJobService;

    @Resource
    private DataSource dataSource;

    private MySqlLatch mySqlLatch;

    private volatile boolean isMaster = false;

    private volatile boolean isActive = true;

    private volatile boolean running = true;

    private ExecutorService executorService;

    private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;

    @Override
    public void destroy() throws Exception {
        this.running = false;
        this.mySqlLatch.stop();
        this.executorService.shutdownNow();
        this.scheduledThreadPoolExecutor.shutdownNow();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.executorService = Executors.newCachedThreadPool();
        this.scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(10);
        this.mySqlLatch = new MySqlLatch(this.dataSource, this);
        this.mySqlLatch.start();
        this.scheduledThreadPoolExecutor.scheduleWithFixedDelay(new ServerRefreshThread(), 0, 5, TimeUnit.SECONDS);
        this.scheduledThreadPoolExecutor.scheduleWithFixedDelay(new ServerCleanThread(), 5, 5, TimeUnit.SECONDS);
    }

    /***
     * 主节点业务操作逻辑
     */
    @Override
    public void isLeader() {
        this.isMaster = true;
        while (this.running) {
            List<SysServer> allServer = sysServerService.getAllServer();
            for (SysServer sysServer : allServer) {
                if (!TimeUtils.isLated(sysServer.getRefreshTime(), Math.negateExact(11))) {
                    logger.warn("server节点:" + sysServer.getId() + " 已掉线 ...");
                    List<Long> allServerIds = CollectionUtils.remove(allServer, sysServer.getId());
                    this.sysServerService.removeById(sysServer.getId());
                    this.sysJobService.updateJobServerId(allServerIds, Arrays.asList(sysServer.getId()));
                }
            }
        }
    }

    /****
     * 节点心跳线程
     */
    class ServerRefreshThread implements Runnable {

        @Override
        public void run() {
            SysServer sysServer = sysServerService.getCurrentSysServer();
            if (sysServer == null) {
                sysServer = new SysServer();
            }
            sysServer.setId(sysServerService.getServerId());
            sysServer.setHost(sysServerService.getIp());
            sysServer.setPort(sysServerService.getPort());
            sysServer.setMaster(isMaster);
            sysServer.setActive(isActive);
            sysServer.recalculateFields();
            sysServerService.saveOrUpdate(sysServer);
        }
    }

    /****
     *
     * 节点任务转移线程
     */
    class ServerCleanThread implements Runnable {

        @Override
        public void run() {
            if (isMaster) {
                List<Long> activeServerIds = sysServerService.getServerIds();
                List<Long> deathServerIds = sysJobService.getDeathServerId(activeServerIds);
                if (!deathServerIds.isEmpty() && !activeServerIds.isEmpty()) {
                    sysJobService.updateJobServerId(activeServerIds, deathServerIds);
                }
            }
        }
    }

    @Override
    public void setMaster(boolean master) {
        this.isMaster = master;
    }

    @Override
    public void setActive(boolean active) {
        this.isActive = active;
    }

    @Override
    public boolean isActive() {
        return isActive;
    }
}
